﻿#include <iostream>
#include <string>
#include "Tools.h"
#include "../VDModule/VDModule.h"
#include <mutex>
#include <thread>
using namespace std;


#ifdef _WIN32
#include <d3d9.h>
#include <dsound.h>
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#ifdef _DEBUG
#ifndef DBG_NEW
#define new new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
#endif
#endif // _DEBUG

#pragma comment(lib,"d3d9.lib")
#pragma comment(lib, "../../bin/VDModule.lib")
#pragma comment(lib, "dsound.lib")
#pragma comment(lib, "dxguid.lib")

MemContext globalMemCtx;
MemContext openMemCtx;
MemContext d3dMemCtx;
MemContext pcmMemCtx;

struct D3DContext
{
	LPDIRECT3D9 pD3D = NULL;
	LPDIRECT3DDEVICE9 pDevice = NULL;
	LPDIRECT3DSURFACE9 pSurface = NULL;
};
D3DContext* d3dctx = NULL;
std::mutex d3dmux;
#endif

void* vdplayer = NULL;
bool isOpen = false;


int width = 3840;
int height = 2160;





bool isPause = false;


void Create()
{
	if (vdplayer)
		printf("创建失败\n");
	else
	{
		vdplayer = CreateVDPlayer(NULL);
	}
}
void Delete()
{
	if (vdplayer)
	{
		DeleteVDPlayer(vdplayer);
		vdplayer = NULL;
	}
}
void demuxcb(void* opaque, DemuxInfo* info)
{
	if (info->retcode == 0)
	{
		width = info->videoStream->pixelwidth;
		height = info->videoStream->pixelheight;
	}
}

void Renderer(Frame* frame)
{

#ifdef _WIN32

	//绘制
	if (!d3dctx->pSurface)
		return;


	D3DLOCKED_RECT d3d_rect;
	HRESULT lRet;
	lRet = d3dctx->pSurface->LockRect(&d3d_rect, NULL, D3DLOCK_NOSYSLOCK);
	if (FAILED(lRet))
	{
		printf("LockRect Failed!\n");
		return;
	}
	//添加数据
	unsigned char* pDest = (unsigned char*)d3d_rect.pBits;
	//direct中是以128字节对齐的
	int stride = d3d_rect.Pitch;
	int ysize = stride * height;
	if (stride == frame->linesize[0])
	{
		//复制数据
		memcpy(pDest, frame->data[0], ysize);
		memcpy(pDest + ysize, frame->data[2], ysize / 4);
		memcpy(pDest + ysize + ysize / 4, frame->data[1], ysize / 4);
	}
	else
	{

		for (int i = 0; i < height; i++)
			memcpy(pDest + i * stride, frame->data[0] + i * frame->linesize[0], frame->linesize[0]);

		unsigned char* vaddr = pDest + ysize;
		for (int i = 0; i < height / 2; i++)
			memcpy(vaddr + i * stride / 2, frame->data[2] + i * frame->linesize[2], frame->linesize[2]);

		unsigned char* uaddr = pDest + ysize + ysize / 4;
		for (int i = 0; i < height / 2; i++)
			memcpy(uaddr + i * stride / 2, frame->data[1] + i * frame->linesize[1], frame->linesize[1]);

	}

	lRet = d3dctx->pSurface->UnlockRect();
	if (FAILED(lRet))
	{
		printf("UnlockRect Failed!\n");
		return;
	}

	d3dctx->pDevice->Clear(
		0,//指向一个矩形数组地数量，与下一个函数有关，如果下一个函数为NULL，则这里为零，反之，下一个参数不是NULL，这里就不能写零
		NULL,//指向一个矩形结构数组地指针
		D3DCLEAR_TARGET,//需要清空地缓冲区，它可以是模板缓冲，颜色缓冲，和深度缓冲三个值地任意组合 
		D3DCOLOR_XRGB(0, 0, 0),////清空缓冲区后每个像素地颜色值
		1.0f,//清空深度缓冲后地每个像素地深度值
		0//清空模板缓冲后地每个像素地模板值
	);

	LPDIRECT3DSURFACE9 pBackBuffer = NULL;
	d3dctx->pDevice->BeginScene();
	d3dctx->pDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
	d3dctx->pDevice->StretchRect(d3dctx->pSurface, NULL, pBackBuffer, NULL, D3DTEXF_LINEAR);
	d3dctx->pDevice->EndScene();
	d3dctx->pDevice->Present(NULL, NULL, NULL, NULL);
	pBackBuffer->Release();
#endif // _WIN32
}

int framecb(void* owner, Frame* frame)
{
	if (frame->isVideoFrame)
	{
#ifdef _WIN32

		d3dmux.lock();
		if (d3dctx)
		{
			Renderer(frame);
		}
		d3dmux.unlock();
#endif
	}
	return 0;
}

void Open(const char* filename)
{
	if (!vdplayer || isOpen)
	{
		printf("打开失败\n");
		return;
	}
	isOpen = true;
	StartDetectMemory(&openMemCtx);

	Open(vdplayer, filename, demuxcb);
}

void Close()
{
	if (!vdplayer || !isOpen) return;
	isOpen = false;
	Close(vdplayer, NULL);
	StopDetectMemory(&openMemCtx);

}

void ReleaseDX()
{
#ifdef _WIN32
	if (!d3dctx) return;

	d3dmux.lock();
	if (d3dctx->pSurface)
		d3dctx->pSurface->Release();
	if (d3dctx->pDevice)
		d3dctx->pDevice->Release();
	if (d3dctx->pD3D)
		d3dctx->pD3D->Release();

	d3dctx->pSurface = NULL;
	d3dctx->pDevice = NULL;
	d3dctx->pD3D = NULL;

	delete d3dctx;
	d3dctx = NULL;
	d3dmux.unlock();
	StopDetectMemory(&d3dMemCtx);
	printf("Direct3D9 clear success\n");
#endif // _WIN32
}

void CreateDX(int wid)
{
#ifdef _WIN32
	if (d3dctx) return;
	StartDetectMemory(&d3dMemCtx);
	d3dmux.lock();
	d3dctx = new D3DContext();

	HWND hwnd = (HWND)wid;

	HRESULT lRet;
	//创建接口对象获取硬件信息并为后面的设备对象做准备
	//这个参数的意义是我们当前的DirectX版本，保证我们包含的头文件能正确调用相应的.dll库。
	//如果失败，说明版本协商有问题
	d3dctx->pD3D = Direct3DCreate9(D3D_SDK_VERSION);
	if (!d3dctx->pD3D)
	{
		d3dmux.unlock();
		printf("Direct3DCreate9 failed!\n");
		ReleaseDX();
		return;
	}

	//用于存储设备信息的结构体
	D3DCAPS9 caps;
	ZeroMemory(&caps, sizeof(D3DCAPS9));
	lRet = d3dctx->pD3D->GetDeviceCaps(
		D3DADAPTER_DEFAULT,	//显卡序号
		D3DDEVTYPE_HAL,		//设备类型
		&caps
	);
	if (FAILED(lRet))
	{
		d3dmux.unlock();
		printf("GetDeviceCaps failed!\n");
		ReleaseDX();
		return;
	}
	//硬件顶点运算模式是要比软件顶点运算模式快很多的
	//显卡支持的定点运算模式
	int vp = 0;
	if (caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;//硬件定点运算模式
	else
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;//软件定点运算模式
	vp |= D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE;

	D3DPRESENT_PARAMETERS d3dparas;
	ZeroMemory(&d3dparas, sizeof(d3dparas));
	d3dparas.Windowed = TRUE;
	d3dparas.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dparas.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dparas.BackBufferWidth = width;
	d3dparas.BackBufferHeight = height;
	d3dparas.BackBufferCount = 2;

	lRet = d3dctx->pD3D->CreateDevice(
		D3DADAPTER_DEFAULT,//显卡序号
		D3DDEVTYPE_HAL,//设备类型，当然是硬件
		hwnd,//窗口句柄
		vp, // 顶点运算模式
		&d3dparas,
		&d3dctx->pDevice////指向我们的设备接口的指针，这个参数就是我们的设备接口
	);
	if (FAILED(lRet))
	{
		d3dmux.unlock();
		printf("CreateDevice failed!\n");
		ReleaseDX();
		return;
	}

	lRet = d3dctx->pDevice->CreateOffscreenPlainSurface(
		width,
		height,
		(D3DFORMAT)MAKEFOURCC('Y', 'V', '1', '2'),
		D3DPOOL_DEFAULT,
		&d3dctx->pSurface,
		NULL
	);
	if (FAILED(lRet))
	{
		d3dmux.unlock();
		printf("CreateOffscreenPlainSurface failed!\n");
		ReleaseDX();
		return;
	}
	d3dmux.unlock();
	printf("Direct3D9 init success!\n");
#endif // _WIN32

}


void Log(int level, const char* log)
{
	printf("log level:%d:%s\n", level, log);
}

void Test()
{
	VDModuleSetLog(Log);
	string str;

	Create();

	while (true)
	{
		printf("Command:");
		getline(cin, str);
		if (!strcmp(str.data(), "exit")) break;
		//处理指令

		if (!str.compare(0, 4, "open"))
		{
			for (int i = 0; i < 4; i++)
				str.erase(str.begin());

			while (true)
			{
				int i = str.find_first_of(' ');
				if (i < 0) break;
				str.erase(str.begin());
			}
			const char* filename = "http://127.0.0.1/4k.mp4";

			str == "" ? Open(filename) : Open(str.data());

		}
		else if (!strcmp(str.data(), "close")) Close();
		else if (!str.compare(0, 4, "crdx"))
		{
			for (int i = 0; i < 4; i++)
				str.erase(str.begin());

			while (true)
			{
				int i = str.find_first_of(' ');
				if (i < 0) break;
				str.erase(str.begin());
			}
			int wid = atoi(str.data());
			if (wid > 0)
				CreateDX(wid);
			else
				CreateDX(0xD70704);

		}
		else if (!strcmp(str.data(), "redx")) ReleaseDX();
		else if (!strcmp(str.data(), "start")) StartDemux(vdplayer, true, true, framecb);
		else if (!str.compare(0, 2, "0.") && str.length() >= 3)
		{
			double v = atof(str.data());
			Seek(vdplayer, v);
		}
		else
			printf("invaild command: %s\n", str.data());
	}
	Delete();
}
int main()
{
	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
	//new char[1254];
	Test();
	_CrtDumpMemoryLeaks();
	return 0;
}